#pragma once

#include<iostream>
#include<string>
#include<vector>
#include<fstream>
#include<unordered_map>
#include"util.hpp"
#include<mutex>
#include"log.hpp"

namespace ns_index
{
  struct DocInfo
  {
    std::string title;
    std::string content;
    std::string url;
    uint64_t doc_id;
  };

  struct InvertedElem
  {
    uint64_t doc_id;
    std::string word;
    int weight;
    InvertedElem():weight(0){}
  };

  typedef std::vector<InvertedElem> InvertedList;

  class Index
  {
    private:
      //正排索引的数据结构用数组，数组的下标天然是文档的ID
      std::vector<DocInfo> forward_index;
      //倒排索引一定是一个关键词和一组（个）InvertedElem对应[关键字和倒排拉链]
      std::unordered_map<std::string,InvertedList> inverted_index;
    public:
      Index(){}
      Index(const Index&) = delete;
      Index& operator=(const Index&) = delete;

      static Index* instance;
      static std::mutex mtx;
    public:
      ~Index(){}
    public:
      static Index* GetInstance()
      {
        if(instance == nullptr)
        {
          mtx.lock();
          if(instance == nullptr)
          {
            instance = new Index();
          }
          mtx.unlock();
        }
        return instance;
      }

      //根据doc_id找到文档内容
      DocInfo *GetForwardIndex(uint64_t doc_id)
      {
        if(doc_id >= forward_index.size())//处理数组越界
        {
          std::cerr<<"doc_id out range,error!"<<std::endl;
          return nullptr;
        }

        return &forward_index[doc_id];
      }
      //根据关键字string，获得倒排拉链
      InvertedList *GetInvertedList(const std::string &word)
      {
        auto iter = inverted_index.find(word);
        if(iter == inverted_index.end())
        {
          std::cerr<<word<<" have no InvertedList!"<<std::endl;
          return nullptr;
        }
        return &(iter->second);
      }
      //根据去标签，格式化之后的文档，建立正排和倒排索引
      bool BuildIndex(const std::string &input)//parse处理完成后的数据交给我
      {
        std::ifstream in(input,std::ios::in | std::ios::binary);
        if(!in.is_open())
        {
          std::cerr<<"sorry, "<<input<<"open error"<<std::endl;
          return false;
        }
        std::string line;
        int count = 0;
        while(std::getline(in,line))
        {
          DocInfo * doc = BuildForwardIndex(line);
          if(doc == nullptr)
          {
            std::cerr<<"build"<<line<<"error"<<std::endl;
            continue;
          }
          BuildInvertedIndex(*doc);
          count++;
          if(count % 50 == 0)
          {
            //std::cout<<"当前已经建立的索引文档："<<count<<std::endl;
            LOG(NORMAL,"当前已经建立的索引文档："+std::to_string(count));
          }
        }

        return true;
      }
  private:
      DocInfo* BuildForwardIndex(const std::string &line)
      {
        //1.解析line，字符串切分
        //line -> 3 string: title,content,url
        std::vector<std::string> results;
        const std::string sep = "\3";
       // ns_util::StringUtil::CutString(line,&results,sep);
       ns_util::StringUtil::Split(line,&results,sep);
        if(results.size() != 3)//切分出错
        {
          return nullptr;
        }
        //2.字符串填充到Doclinfo
        DocInfo doc;
        doc.title = results[0];
        doc.content = results[1];
        doc.url = results[2];
        doc.doc_id = forward_index.size();

        //3.插入到正排索引的vector
        forward_index.push_back(std::move(doc));

        return &forward_index.back();
      }


      bool BuildInvertedIndex(const DocInfo &doc)
      {
        struct word_cnt
        {
          int title_cnt;
          int content_cnt;

          word_cnt():title_cnt(0),content_cnt(0){}
        };
        std::unordered_map<std::string,word_cnt> word_map;//用来暂存词频的映射表

        //对标题进行分词
        std::vector<std::string> title_words;
        ns_util::JiebaUtil::CutString(doc.title,&title_words);

        //对标题进行词频统计
        for(std::string s : title_words)
        {
          boost::to_lower(s);//统一转化为小写
          word_map[s].title_cnt++;//如果存在就获取，如果不存在就新建
        }

        //对文档内容进行分词
        std::vector<std::string> content_words;
        ns_util::JiebaUtil::CutString(doc.content,&content_words);

        //对内容进行词频统计
        for(std::string s : content_words)
        {
          boost::to_lower(s);//统一转化为小写
          word_map[s].content_cnt++;
        }
#define X 10
#define Y 1
        for(auto &word_pair : word_map)
        {
          InvertedElem item;
          item.doc_id = doc.doc_id;
          item.word = word_pair.first;
          item.weight = X*word_pair.second.title_cnt + Y*word_pair.second.content_cnt; 
          InvertedList &Inverted_list = inverted_index[word_pair.first];
          Inverted_list.push_back(std::move(item));
        }   
          return true;
      }
  };
  Index* Index::instance = nullptr;
  std::mutex Index::mtx;
}
